// 版权所有2016 Go作者。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// go:build ignore 
// +build ignore 

// 此程序通过“go generate”（通过sort.go中的指令）
// 运行以生成zfuncversion.go。
// 
// 它复制sort.go至zfuncversion.go，仅保留
// 采用“数据接口”参数的函数，并重命名每个函数，使其具有
// “_func”后缀，并改为采用“无数据SSwap”。然后，它重写
// 对适当的_func变量的每个内部函数调用。

package main

import (
	"bytes"
	"go/ast"
	"go/format"
	"go/parser"
	"go/token"
	"log"
	"os"
	"regexp"
)

var fset = token.NewFileSet()

func main() {
	af, err := parser.ParseFile(fset, "sort.go", nil, 0)
	if err != nil {
		log.Fatal(err)
	}
	af.Doc = nil
	af.Imports = nil
	af.Comments = nil

	var newDecl []ast.Decl
	for _, d := range af.Decls {
		fd, ok := d.(*ast.FuncDecl)
		if !ok {
			continue
		}
		if fd.Recv != nil || fd.Name.IsExported() {
			continue
		}
		typ := fd.Type
		if len(typ.Params.List) < 1 {
			continue
		}
		arg0 := typ.Params.List[0]
		arg0Name := arg0.Names[0].Name
		arg0Type := arg0.Type.(*ast.Ident)
		if arg0Name != "data" || arg0Type.Name != "Interface" {
			continue
		}
		arg0Type.Name = "lessSwap"

		newDecl = append(newDecl, fd)
	}
	af.Decls = newDecl
	ast.Walk(visitFunc(rewriteCalls), af)

	var out bytes.Buffer
	if err := format.Node(&out, fset, af); err != nil {
		log.Fatalf("format.Node: %v", err)
	}

	// 删除注释后去掉空行。
	src := regexp.MustCompile(`\n{2,}`).ReplaceAll(out.Bytes(), []byte("\n"))

	// 为丢失的读者在每个函数中添加注释。
	// 这比通过AST添加评论和尝试获得正确的职位信息要容易得多。
	src = regexp.MustCompile(`(?m)^func (\w+)`).ReplaceAll(src, []byte("\n// sort.go的自动生成变量：$1\nfunc${1}\u func）

	// 最终gofmt。
	src, err = format.Source(src)
	if err != nil {
		log.Fatalf("format.Source: %v on\n%s", err, src)
	}

	out.Reset()
	out.WriteString(`// 使用genzfunc.go从sort.go生成的代码；不要编辑。

// 版权所有2016 Go作者。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可在许可证文件中找到。

`)
	out.Write(src)

	const target = "zfuncversion.go"
	if err := os.WriteFile(target, out.Bytes(), 0644); err != nil {
		log.Fatal(err)
	}
}

type visitFunc func(ast.Node) ast.Visitor

func (f visitFunc) Visit(n ast.Node) ast.Visitor { return f(n) }

func rewriteCalls(n ast.Node) ast.Visitor {
	ce, ok := n.(*ast.CallExpr)
	if ok {
		rewriteCall(ce)
	}
	return visitFunc(rewriteCalls)
}

func rewriteCall(ce *ast.CallExpr) {
	ident, ok := ce.Fun.(*ast.Ident)
	if !ok {
		// 例如，跳过SelectorExpr（data.Less（..）调用）
		return
	}
	// 跳过强制转换
	if ident.Name == "int" || ident.Name == "uint" {
		return
	}
	if len(ce.Args) < 1 {
		return
	}
	ident.Name += "_func"
}
